﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FFmpegCutVideo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //剪切视频
        private async void BtnCutVideo_Click(object sender, EventArgs e)
        {

            if (this.txtMin.Text == "" && this.txtSec.Text == ""&&this.txtHour.Text=="")
            {
                MessageBox.Show("起始时间的小时、分钟和秒不能同时为空。");
                return;
            }
                
            if (this.txtNewFileName.Text == "") return;
            if (!File.Exists(textBoxFileName.Text))
            {
                MessageBox.Show("文件不存在。请检查一下。");
                return;
            }
            if (File.Exists(txtNewFileName.Text))
            {
                MessageBox.Show("存在同名文件");
                return;
            }
            //参数
            string _ss = " -ss "+ (txtHour.Text == "" ? "00" : txtHour.Text)+":" + (txtMin.Text == "" ? "00" : txtMin.Text) + ":" + (txtSec.Text == "" ? "00" : txtSec.Text);
            string _t;
            if (string.IsNullOrEmpty(txtHourEnd.Text)&& string.IsNullOrEmpty(txtMinEnd.Text)&& string.IsNullOrEmpty(txtSecEnd.Text))
            {
                _t = " -t 09:59:59";
            }
            else
            {
                _t=" -t "+ (txtHourEnd.Text == "" ? "00" : txtHourEnd.Text)+":" + (txtMinEnd.Text == "" ? "00" : txtMinEnd.Text) + ":" + (txtSecEnd.Text == "" ? "00" : txtSecEnd.Text);
            }
            
            string _i = " -accurate_seek -i " + "\"" + this.textBoxFileName.Text + "\""; //在i参数之前加-accurate_seek，据说是能更精准的剪切，试过后，发现并不能.加与不加效果一样。
            string _copy = " -vcodec copy -acodec copy "; //-avoid_negative_ts 1。加上后，会影响精准性。它的意义应该是 转变时间戳shift timestamps。
            string _destName = txtNewFileName.Text;

            string strArguments = _ss + _t + _i + _copy + "\"" + _destName + "\"";
            Debug.Print(strArguments);
            this.BtnCutVideo.Enabled = false;
            
            await Task.Run(()=>FuncFFmpeg(strArguments));//调用方法
            
            this.BtnCutVideo.Enabled = true;
        }

        //加载
        private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;//不必检查跨线程
        }

        //将文件拖动到窗口，获得文件的全路径。
        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.All;
            else
                e.Effect = DragDropEffects.None;
        }

        //获得路径
        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            string path = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            textBoxFileName.Text = path;
            txtNewFileName.Text = SetNewFileName(path);
            this.txtHour.Text = this.txtMin.Text = this.txtSec.Text = this.txtHourEnd.Text = this.txtMinEnd.Text = this.txtSecEnd.Text = "";

        }

        //调整视频比例
        private void BtnChangeRate_Click(object sender, EventArgs e)
        {
            if (this.comboBoxRatio.Text == "") return;
            if (this.txtNewFileName.Text == "") return;
            if (!File.Exists(textBoxFileName.Text))
            {
                MessageBox.Show("文件不存在。请检查一下。");
                return;
            }
            if (File.Exists(txtNewFileName.Text))
            {
                MessageBox.Show("存在同名文件");
                return;
            }

            //参数：.\ffmpeg  -i 松松.mp4 -vcodec copy -acodec copy -aspect 16:9 松松-out.mp4	
            //-aspect 调整视频播放的纵横比：屏幕比例
            //用-s参数设置视频分辨率，参数值wxh，w宽度单位是像素，h高度单位是像素

            string _i = " -i " + "\"" + this.textBoxFileName.Text + "\"";
            string _aspect = " -vcodec copy -acodec copy -aspect ";
            string _s = " -vf scale=";
            string _ratio = this.comboBoxRatio.Text;
            string _destName = " \"" + txtNewFileName.Text + "\"";
            //根据是否有X来判断，操作是调整播放纵横比还是视频的分辨率。
            string strArguments;
            if (this.comboBoxRatio.Text.ToLower().Contains("x"))
            {
                strArguments = _i + _s + _ratio +  _destName;

            }
            else
            {
                strArguments = _i + _aspect + _ratio + _destName;
            }

            Debug.Print(strArguments);
            this.BtnChangeRate.Enabled = false;
            FuncFFmpeg(strArguments);//调用方法
            this.BtnChangeRate.Enabled = true;
        }

        //选择文件
        private void BtnSelectFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog
            {
                Title = "请选择要处理的视频文件",
                Filter = "视频文件|*.avi;*.mp4;*.mkv;*.mpg;*.mov;*.rmvb;*.ts"
            };
            ofd.ShowDialog();
            if (ofd.FileName == "") return;
            this.textBoxFileName.Text = ofd.FileName;
            txtNewFileName.Text = SetNewFileName(this.textBoxFileName.Text);
            this.txtHour.Text = this.txtMin.Text = this.txtSec.Text = this.txtHourEnd.Text = this.txtMinEnd.Text = this.txtSecEnd.Text = "";
        }

        private string[] videoNames = null;//准备存多个文件路径的数组
        //多选文件
        private void BtnSelectFiles_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog
            {
                Title = "请选择要处理的视频文件",
                Multiselect = true,
                Filter = "视频文件|*.avi;*.mp4;*.mkv;*.mpg;*.mov;*.rmvb;*.ts;*.wmv"
            };
            ofd.ShowDialog();
            if (ofd.FileName == "") return;
            videoNames = ofd.FileNames;//将文件全路径存到数组中。
            
            List<string> videoNamesNullDir=new List<string>();
            foreach (string item in videoNames)
            {
                videoNamesNullDir.Add(Path.GetFileNameWithoutExtension(item));
            }

            this.textBoxFileMulti.Text = string.Join("|", videoNamesNullDir.ToArray());
            txtNewFileName.Text = SetNewFileName(videoNames[0]);
        }

        //合并多个同类视频
        private void BtnConcat_Click(object sender, EventArgs e)
        {
            if (this.textBoxFileMulti.Text == "") return;
            if (this.txtNewFileName.Text == "") return;
            if (File.Exists(txtNewFileName.Text))
            {
                MessageBox.Show("存在同名文件");
                return;
            }

            //先向路径中的file_List.txt文件中，写入要合并视频的路径文件名，然后再调用这个文本文件。
            //判断txt文件是否存在。
            if (!File.Exists("file_List.txt"))
            {
                MessageBox.Show("缺少file_List.txt文件。可以新建一个空文件。");
                return;
            }

            //向文本文件中写入数据（覆盖）
            using (StreamWriter writer = new StreamWriter("file_List.txt"))
            {
                foreach (string item in videoNames)
                {
                    writer.WriteLine("file '" + item + "'");
                }
                
            }

            //参数：.\ffmpeg  -f concat -i list.txt -c copy concat.mp4
            string _f = " -f concat -safe 0";
            string _i = " -i file_List.txt";
            string _c = " -c copy";
            string _destName = " \"" + txtNewFileName.Text + "\"";
            string strArguments = _f + _i + _c + _destName;
            Debug.Print(strArguments);
            this.BtnConcat.Enabled = false;
            FuncFFmpeg(strArguments);//调用方法
            this.BtnConcat.Enabled = true;
        }




        #region 方法组

        /// <summary>
        /// 方法：调用FFmpeg进行操作视频
        /// </summary>
        /// <param name="strArguments">传递的参数</param>
        private void FuncFFmpeg(string strArguments)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Process p = new Process();

            p.StartInfo.FileName = @".\ffmpeg.exe";//FFmpeg的路径
            p.StartInfo.Arguments = strArguments;//参数
            p.StartInfo.UseShellExecute = false;//是否使用操作系统shell启动,一定要为false

            //把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的
            //这是作者耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的)
            p.StartInfo.RedirectStandardError = true;

            p.StartInfo.CreateNoWindow = true;//不显示程序窗口
            p.ErrorDataReceived += new DataReceivedEventHandler(Output);
            p.Start();//启动线程

            p.BeginErrorReadLine();//开始异步读取
            p.WaitForExit();//阻塞等待进程结束
            p.Close();//关闭进程
            p.Dispose();//释放资源


            watch.Stop();
            TimeSpan time = watch.Elapsed;
            MessageBox.Show("完成。共计用时：" + time.TotalSeconds + "秒");
        }

        /// <summary>
        /// 方法：根据原文件名生成新的文件名
        /// </summary>
        /// <param name="text">原文件名与路径</param>
        /// <returns></returns>
        private string SetNewFileName(string text)
        {
            string path = Path.GetDirectoryName(text);
            string fileName = Path.GetFileNameWithoutExtension(text);
            string extension = Path.GetExtension(text);
            if (fileName.ToLower().EndsWith("c"))
            {
                fileName = fileName.Substring(0, fileName.Length - 1) + "-C-out";
            }
            else if (fileName.ToLower().EndsWith("ch"))
            {
                fileName = fileName.Substring(0, fileName.Length - 2) + "-C-out";
            }
            else
            {
                fileName += "-out";
            }

            return path + "\\" + fileName + extension;
        }

        /// <summary>
        /// 方法：FFmpeg输出
        /// </summary>
        /// <param name="sendProcess"></param>
        /// <param name="output"></param>
        private void Output(object sendProcess, DataReceivedEventArgs output)
        {
            if (!String.IsNullOrEmpty(output.Data))
            {
                this.textBoxResult.Text = output.Data.ToString();
            }
        }






        #endregion


    }
}
